﻿#include "pch.h"
#if _QG_USE_D11
#include "qgdrv_d11_rdh.h"
#include "qgdrv_d11_cnv.h"
#include "qg_blits.h"

//
qgDepthBuffer* D11Rdh::CreateDepthBuffer(kuint width, kuint height)
{
	RDHNFO_INVOKE();

	kconstpointer key = K_CAST_PUINT_TO_PTR(D11DepthBuffer::MakeKey(width, height));

	D11DepthBuffer* base = (D11DepthBuffer*)_keep.depthbuffers.Get(key);
	if (base != NULL)
		base->Load();
	else
	{
		HRESULT hr;

		// DXGI_FORMAT_D24_UNORM_S8_UINT - 32비트 24깊이 8스텐실
		// DXGI_FORMAT_D32_FLOAT - 32비트 깊이
		// DXGI_FORMAT_D32_FLOAT_S8X24_UINT - 64비트? 32깊이 8스텐실 24안씀

		//
		D3D11_TEXTURE2D_DESC td;
		td.Width = width;
		td.Height = height;
		td.MipLevels = 1;
		td.ArraySize = 1;	// 큐브 지원안함 오직 평평
		td.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
		td.SampleDesc = _msaa;
		td.Usage = D3D11_USAGE_DEFAULT;
		td.BindFlags = D3D11_BIND_DEPTH_STENCIL;
		td.CPUAccessFlags = 0;
		td.MiscFlags = 0;	// 큐브 지원안함 (D3D11_RESOURCE_MISC_TEXTURECUBE)

		ID3D11Texture2D* tex;
		hr = _device->CreateTexture2D(&td, NULL, &tex);

		if (FAILED(hr))
			return false;

		//
		D3D11_DEPTH_STENCIL_VIEW_DESC dsvd;
		memset(&dsvd, 0, sizeof(D3D11_DEPTH_STENCIL_VIEW_DESC));
		dsvd.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
		dsvd.ViewDimension = (td.SampleDesc.Count > 1) ? D3D11_DSV_DIMENSION_TEXTURE2DMS : D3D11_DSV_DIMENSION_TEXTURE2D;

		ID3D11DepthStencilView* dsv;
		hr = _device->CreateDepthStencilView(tex, &dsvd, &dsv);

		tex->Release();

		if (FAILED(hr))
			return false;

		//
		D11DepthBuffer* base = kobj_create(D11DepthBuffer);

		base->_node_desc = K_CAST_PTR_TO_PUINT(dsv);
		base->_width = width;
		base->_height = height;
		_keep.depthbuffers.Add(base);
	}

	return base;
}

//
qgTexture* D11Rdh::CreateTexture(const char* key)
{
	RDHNFO_INVOKE();
	
	D11TextureBase* tex;
	if ((tex = (D11TextureBase*)_keep.texture2s.Get(key)) != NULL)
		tex->Load();
	else if ((tex = (D11TextureBase*)_keep.texture3s.Get(key)) != NULL)
		tex->Load();
	else if ((tex = (D11TextureBase*)_keep.animtextures.Get(key)) != NULL)
		tex->Load();
	return tex;
}

//
qgTexture* D11Rdh::CreateTexture2(const char* key, qgImage* img, kint flags)
{
	RDHNFO_INVOKE();

	k_return_value_if_fail(img != NULL, NULL);

	D11Texture2* tex;

	if (key != NULL)
	{
		tex = (D11Texture2*)_keep.texture2s.Get(&key);
		if (tex != NULL)
			return NULL;
	}

	tex = kobj_create(D11Texture2);
	if (tex->_InitDimension(_caps.max_texture_dimension, img->GetWidth(), img->GetHeight(), 1, 0, img->GetColorFormat()) &&
		tex->_InitProperty(this, key, QGTEXTURE_2D, flags) &&
		tex->_Create(this, img))
		_keep.texture2s.Add(tex);
	else
	{
		tex->Unload();
		tex = NULL;
	}

	return tex;
}

//
qgTexture* D11Rdh::CreateTexture2(qgColorFormat fmt, kuint width, kuint height, kuint mips, kint flags, const char* key)
{
	RDHNFO_INVOKE();

	D11Texture2* tex;

	if (key != NULL)
	{
		tex = (D11Texture2*)_keep.texture2s.Get(&key);
		if (tex != NULL)
			return NULL;
	}

	if (K_OFLAG(flags, QGTEXF_OFF))
	{
		D11OffTexture* off = kobj_create(D11OffTexture);
		if (off->_InitDimension(_caps.max_texture_dimension, width, height, 1, mips, fmt) &&
			off->_InitProperty(this, key, QGTEXTURE_2D, flags) &&
			off->_Create(this, false, NULL) &&
			off->_CreateOff(this))
		{
			tex = off;
			_keep.texture2s.Add(tex);
		}
		else
		{
			off->Unload();
			tex = NULL;
		}
	}
	else
	{
		tex = kobj_create(D11Texture2);
		if (tex->_InitDimension(_caps.max_texture_dimension, width, height, 1, mips, fmt) &&
			tex->_InitProperty(this, key, QGTEXTURE_2D, flags) &&
			tex->_Create(this, false, NULL))
			_keep.texture2s.Add(tex);
		else
		{
			tex->Unload();
			tex = NULL;
		}
	}

	return tex;
}

//
qgTexture* D11Rdh::CreateTexture3(qgColorFormat fmt, kuint width, kuint height, kuint depth, kuint mips, kint flags, const char* key)
{
	RDHNFO_INVOKE();

	if (K_OFLAG(flags, QGTEXF_OFF))
		return NULL;

	D11Texture3* tex;

	if (key != NULL)
	{
		tex = (D11Texture3*)_keep.texture3s.Get(&key);
		if (tex != NULL)
			return NULL;
	}

	tex = kobj_create(D11Texture3);
	if (tex->_InitDimension(_caps.max_texture_dimension, width, height, depth, mips, fmt) &&
		tex->_InitProperty(this, key, QGTEXTURE_3D, flags) &&
		tex->_Create(this))
		_keep.texture3s.Add(tex);
	else
	{
		tex->Unload();
		tex = NULL;
	}

	return tex;
}

//
qgTexture* D11Rdh::CreateTextureCube(qgColorFormat fmt, kuint size, kuint mips, kint flags, const char* key)
{
	RDHNFO_INVOKE();

	if (K_OFLAG(flags, QGTEXF_OFF))
		return NULL;

	D11Texture2* tex;

	if (key != NULL)
	{
		tex = (D11Texture2*)_keep.texture2s.Get(&key);
		if (tex != NULL)
			return NULL;
	}

	tex = kobj_create(D11Texture2);
	if (tex->_InitDimension(_caps.max_texture_dimension, size, size, size, mips, fmt) &&
		tex->_InitProperty(this, key, QGTEXTURE_CUBE, flags) &&
		tex->_Create(this, true, NULL))
		_keep.texture2s.Add(tex);
	else
	{
		tex->Unload();
		tex = NULL;
	}

	return tex;
}

//
qgTexture* D11Rdh::CreateAnimTexture(const char* filename, bool allocate /* = false */)
{
	RDHNFO_INVOKE();

	k_return_value_if_fail(filename != NULL, NULL);

	D11AnimTexture* tex = (D11AnimTexture*)_keep.animtextures.Get(&filename);
	if (tex != NULL)
	{
		tex->Load();
		return tex;
	}

	tex = kobj_create(D11AnimTexture);
	if (tex->_CreateAnim(filename, allocate, this))
		_keep.animtextures.Add(tex);
	else
	{
		tex->Unload();
		tex = NULL;
	}

	return tex;
}


//////////////////////////////////////////////////////////////////////////
// 뎁스 버퍼

//
void D11DepthBuffer::TypeInitialize(kType type) throw()
{
	kTsb t(type);
	t.SetFuncKey(&this_type::BaseKey);
	t.SetFuncEq(&this_type::BaseEq);
	t.SetFuncHash(&this_type::KeyHash);
}

//
D11DepthBuffer::~D11DepthBuffer() throw()
{
	if (_node_desc != 0)
	{
		ID3D11DepthStencilView* dsv = (ID3D11DepthStencilView*)_node_desc;
		dsv->Release();
	}

	((D11Rdh*)qgRdh::Instance)->_RoamDepthBufferUnload(this);
}

//
kconstpointer D11DepthBuffer::BaseKey(kNodeBase* ptr)
{
	D11DepthBuffer* self = (D11DepthBuffer*)ptr;
	return K_CAST_PUINT_TO_PTR(MakeKey(self->_width, self->_height));
}

//
bool D11DepthBuffer::BaseEq(kNodeBase* ptr, kconstpointer k2)
{
	D11DepthBuffer* self = (D11DepthBuffer*)ptr;
	kconstpointer k1 = K_CAST_PUINT_TO_PTR(MakeKey(self->_width, self->_height));
	return k1 == k2;
}

//
ksize_t D11DepthBuffer::KeyHash(kconstpointer key)
{
	return K_CAST_PTR_TO_PUINT(key);
}

//
ksize_t D11DepthBuffer::MakeKey(kuint width, kuint height)
{
#if _SB_64_
	kvlong vk;
	vk.dw.l = width;
	vk.dw.h = height;
	return vk.q;
#else
	kvint vk;
	vk.w.l = (kushort)width;
	vk.w.h = (kushort)height;
	return vk.dw;
#endif
}


//////////////////////////////////////////////////////////////////////////
// 텍스쳐 베이스

//
void D11TextureBase::TypeInitialize(kType type) throw()
{
	kTsb t(type);
	t.SetFuncKey(&this_type::BaseKey);
	t.SetFuncEq((khEqFunc)&this_type::BaseEq);
	t.SetFuncHash((khHashFunc)&this_type::KeyHash);
}

//
D11TextureBase::~D11TextureBase() throw()
{
	if (_node_desc != 0)
	{
		ID3D11ShaderResourceView* srv = (ID3D11ShaderResourceView*)_node_desc;
		srv->Release();
	}

	kunk_release(_dev_tex);
	k_delete(_key);
}

//
bool D11TextureBase::_InitDimension(kuint maxsize, kuint width, kuint height, kuint depth, kuint mips, qgColorFormat fmt)
{
	_fmt = fmt == QGCF_NONE ? QGCF32_RGBA : fmt;
	_insts.alpha = D11Convert::DetectColor(_fmt, _dev_fmt);

	if (_dev_fmt == DXGI_FORMAT_UNKNOWN)
	{
		// 지원 안함
		return false;
	}

	if (width < maxsize)
		_width = width == 0 ? 1 : width;
	else
	{
		_width = maxsize;
		_insts.adj_clamp = true;
	}

	if (height < maxsize)
		_height = height == 0 ? 1 : height;
	else
	{
		_height = maxsize;
		_insts.adj_clamp = true;
	}

	if (depth < maxsize)
		_depth = depth == 0 ? 1 : depth;
	else
	{
		_depth = maxsize;
		_insts.adj_clamp = true;
	}

	if (mips == 0)
	{
		_mips = 0;
		_insts.mip_auto = true;
		_insts.mip_has = true;
	}
	else
	{
		_mips = (kushort)mips;
		_insts.mip_auto = false;
		_insts.mip_has = mips > 1;
	}

	return true;
}

//
bool D11TextureBase::_InitProperty(D11Rdh* rdh, const char* name, qgTextureType type, kint flags)
{
	_key = name && *name ? k_strdup(name) : k_apsprintf("d11_texture_%Lu", k_nth());
	_type = type;
	_dev_sdc.Count = 1;
	_dev_sdc.Quality = 0;

	// 플래그 조사 등등
	if (K_OFLAG(flags, QGRESF_DYNAMIC))
	{
		if (K_OFLAG(flags, QGTEXF_OFF))
		{
			// 다이나믹 + 오프 금지
			return false;
		}

		if (!K_OFLAG(flags, QGTEXF_NOMIP))
		{
			// 다이나믹 -> 밉맵 금지
			_insts.mip_has = false;
		}

		_mips = 1;
		_insts.dynamic = true;
	}
	else
	{
		if (K_OFLAG(flags, QGTEXF_OFF))
		{
			if (!K_OFLAG(flags, QGTEXF_NOMIP))
			{
				// 오프 -> 밉맵 금지
				_insts.mip_has = false;
			}

			_dev_sdc = rdh->GetMsaa();
			_insts.beta_off = true;
			_mips = 1;
		}
		else
		{
			if (!K_OFLAG(flags, QGTEXF_NOMIP))
			{
				// 자동 밉맵이 되나 검사
				UINT supp = 0;
				if (SUCCEEDED(rdh->GetDevice()->CheckFormatSupport(_dev_fmt, &supp)) &&
					K_OFLAG(supp, D3D11_FORMAT_SUPPORT_MIP_AUTOGEN))
				{
					_insts.mip_auto = true;
					_mips = 0;
				}
				else
				{
					// 수동 밉맵은 지원안하니깐 1개
					_mips = 1;
				}
			}
			else
			{
				if (_mips == 0)
					_mips = 1;
			}
		}
	}

	if (K_OFLAG(flags, QGTEXF_IMAGE))	// 이미지 데이터 갖고 있기
		_insts.image_keep = true;

	return true;
}

//
kconstpointer D11TextureBase::BaseKey(kNodeBase* ptr)
{
	D11TextureBase* self = (D11TextureBase*)ptr;
	return &self->_key;
}

//
bool D11TextureBase::BaseEq(kNodeBase* ptr, const Key& k2)
{
	D11TextureBase* self = (D11TextureBase*)ptr;
	const Key& k1 = self->_key;
	return k_stricmp(k1, k2) == 0;
}

//
ksize_t D11TextureBase::KeyHash(const Key& key)
{
	return k_strihash(key);
}


//////////////////////////////////////////////////////////////////////////
// 텍스쳐2

//
D11Texture2::D11Texture2() throw()
{
	_lock_mip = -1;
}

// 
D11Texture2::~D11Texture2() throw()
{
	kobj_unload(_img_raw);
	kobj_unload(_img_mip);

	((D11Rdh*)qgRdh::Instance)->_RoamTexture2Unload(this);
}

//
bool D11Texture2::_Create(D11Rdh* rdh, bool iscube, const D3D11_SUBRESOURCE_DATA* subres)
{
	//
	D3D11_TEXTURE2D_DESC txd;
	txd.Width = _width;
	txd.Height = _height;
	txd.MipLevels = _mips;	// 자동밉맵이면 0일 것이다. 아니면 큰일남.
	txd.Format = _dev_fmt;
	txd.SampleDesc = _dev_sdc;
	if (_insts.dynamic)
	{
		txd.Usage = D3D11_USAGE_DYNAMIC;
		txd.BindFlags = D3D11_BIND_SHADER_RESOURCE;
		txd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
		txd.MiscFlags = 0;
	}
	else
	{
		txd.Usage = D3D11_USAGE_DEFAULT;
		txd.CPUAccessFlags = 0;
		if (_insts.beta_off)
		{
			txd.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
			txd.MiscFlags = 0;
		}
		else
		{
			if (!_insts.mip_auto)
			{
				txd.BindFlags = D3D11_BIND_SHADER_RESOURCE;
				txd.MiscFlags = 0;
			}
			else
			{
				txd.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
				txd.MiscFlags = D3D11_RESOURCE_MISC_GENERATE_MIPS;
			}
		}
	}
	if (!iscube)
		txd.ArraySize = 1;
	else
	{
		txd.MiscFlags |= D3D11_RESOURCE_MISC_TEXTURECUBE;
		txd.ArraySize = 6;
	}

	ID3D11Device* device = rdh->GetDevice();
	if (FAILED(device->CreateTexture2D(&txd, subres, (ID3D11Texture2D**)&_dev_tex)))
	{
		kobj_tracep(KLV_ERROR, KERR_FAIL_CREATE);
		return false;
	}

	//
	D3D11_SHADER_RESOURCE_VIEW_DESC srvd;
	srvd.Format = _dev_fmt;
	srvd.Texture2D.MostDetailedMip = 0;
	srvd.Texture2D.MipLevels = _mips;
	if (iscube)
		srvd.ViewDimension = D3D_SRV_DIMENSION_TEXTURECUBE;
	else
	{
		srvd.ViewDimension = _dev_sdc.Count > 1 ? D3D_SRV_DIMENSION_TEXTURE2DMS : D3D_SRV_DIMENSION_TEXTURE2D;
		if (_mips == 0)
		{
			srvd.Texture2D.MipLevels = (UINT)-1;

			D3D11_TEXTURE2D_DESC tmp;
			((ID3D11Texture2D*)_dev_tex)->GetDesc(&tmp);
			_mips = (kushort)tmp.MipLevels;
		}
	}

	ID3D11ShaderResourceView* srv;
	if (FAILED(device->CreateShaderResourceView(_dev_tex, &srvd, &srv)))
	{
		kobj_tracep(KLV_ERROR, KERR_FAIL_CREATE);
		return false;
	}

	_node_desc = K_CAST_PTR_TO_PUINT(srv);

	return true;
}

//
bool D11Texture2::_Create(D11Rdh* rdh, qgImage* img)
{
	if (!_insts.adj_pow2 || !_insts.adj_clamp)
	{
		// D11은 BGRA로 읽기 때문에 걍 씀
		_img_raw = img;
		img->Load();
	}
	else
	{
		_img_raw = kobj_create(qgImage);
		_img_raw->Initialize(_width, _height, _fmt);
		img->CopyToScale(_img_raw);
	}

	if (_insts.mip_auto)
	{
		if (!_Create(rdh, false, NULL))
			return false;

		ID3D11DeviceContext* context = rdh->GetContext();
		context->UpdateSubresource(_dev_tex, 0, NULL,
			_img_raw->GetData(), _img_raw->GetPitch(), _img_raw->GetSize());
		context->GenerateMips(CastDescription<ID3D11ShaderResourceView>());
	}
	else
	{
		D3D11_SUBRESOURCE_DATA subres;
		subres.pSysMem = _img_raw->GetData();
		subres.SysMemPitch = _img_raw->GetPitch();
		subres.SysMemSlicePitch = _img_raw->GetSize();
		if (!_Create(rdh, false, &subres))
			return false;
	}

	return true;
}

//
bool D11Texture2::Map(qbTextureMap* map, kint flag, kint mips, kint sliceface)
{
	k_return_value_if_fail(map != NULL, false);
	k_return_value_if_fail(_lock_mip < 0, false);
	k_return_value_if_fail(_dev_tex != NULL, false);

	if ((kuint)mips >= _mips)
	{
		kobj_tracep(KLV_ERROR, KERR_OVERFLOW);
		return false;
	}

	if (sliceface < 0)
		sliceface = 0;

	// 다이나믹이면 그냥 잠근 내용을 보내자, 어짜피 WDONLY다.
	if (_insts.dynamic)
	{
		ID3D11DeviceContext* context = ((D11Rdh*)qgRdh::Instance)->GetContext();

		// D3D11CalcSubresource(밉맵 번호, 면 번호, 총 밉맵개수)
		UINT cs = D3D11CalcSubresource(mips, sliceface, _mips);
		D3D11_MAP flag = D3D11_MAP_WRITE_DISCARD;
		D3D11_MAPPED_SUBRESOURCE ms;

		if (FAILED(context->Map(_dev_tex, cs, flag, 0, &ms)))
			return false;

		// 정리
		map->data = (kbyte*)ms.pData;
		map->pitch = (kint)ms.RowPitch;
		map->slice = (kint)ms.DepthPitch;
		map->width = _width;
		map->height = _height;
		map->depth = _depth;
	}
	else
	{
		qgImage* img = _img_raw;

		// 이미지가 없으면 만들기... 오프 포함. 참고로 오프는 다이나믹일 수 없다.
		if (!img || _insts.beta_off)
		{
			if (!img)
			{
				if (mips == 0)
				{
					img = _img_raw = kobj_create(qgImage);
					img->Initialize(_width, _height, _fmt);
				}
				else
				{
					int i = 0, w = _width, h = _height;
					do
					{
						if (w > 1) w >>= 1;
						if (h > 1) h >>= 1;
					} while (i != mips);
					img = _img_mip = kobj_create(qgImage);
					img->Initialize(w, h, _fmt);
				}
			}

			// 이미지 다운로드... 하지 말자 귀찮다
			if (flag == QGENTF_READ || flag == QGENTF_READWRITE)
			{
				// 그래도 모르니 ScreenCapture() 함수를 참고로 만들면 된다
			}
		}

		// 정리
		map->data = img->GetData();
		map->pitch = img->GetPitch();
		map->slice = 0;
		map->width = img->GetWidth();
		map->height = img->GetHeight();
		map->depth = 0;
	}

	// 저장
	_lock_mip = mips;
	_lock_slice = sliceface;

	return true;
}

//
bool D11Texture2::Unmap()
{
	k_return_value_if_fail(_lock_mip >= 0, false);

	// 
	ID3D11DeviceContext* context = ((D11Rdh*)qgRdh::Instance)->GetContext();

	// D3D11CalcSubresource(밉맵 번호, 면 번호, 총 밉맵개수)
	UINT cs = D3D11CalcSubresource(_lock_mip, _lock_slice, _mips);

	// 다이나믹이면 그냥 품
	if (_insts.dynamic)
	{
		// 딸랑 이거 하나임
		context->Unmap(_dev_tex, cs);
	}
	else
	{
		// 그냥? 아님 밉맵?
		qgImage* img = _img_mip ? _img_mip : _img_raw;
		k_return_value_if_fail(img, false);

		// 압축 텍스쳐는 좀 다르다더라
		const qbPixelProp& prop = img->GetPixelProp();
		UINT pitch;

		if ((prop.flags & QGCFF_COMPRESS) == 0)
			pitch = img->GetPitch();
		else
		{
			if (prop.fmt == QGCF_DXT1)
				pitch = (img->GetWidth() / 4) * 8;
			else
				pitch = (img->GetWidth() / 4) * 16;
		}

		// 올리고
		D3D11_BOX dbox =
		{
			// left, top, front
			0, 0, 0,
			// right, bottom, back
			img->GetWidth(), img->GetHeight(), 1
		};
		context->UpdateSubresource(_dev_tex, cs, &dbox, img->GetData(), pitch, 0);

		// 자동 밉맵
		if (_insts.mip_auto && _lock_mip == 0)
		{
			ID3D11ShaderResourceView* srv = (ID3D11ShaderResourceView*)_node_desc;
			context->GenerateMips(srv);
		}

		// 안쓰는 이미지 처리
		if (_img_mip)
		{
			_img_mip->Unload();
			_img_mip = NULL;
		}
		else if (!_insts.image_keep)
		{
			_img_raw->Unload();
			_img_raw = NULL;
		}
	}

	// 정리
	_lock_mip = -1;
	_lock_slice = 0;

	return true;
}


//////////////////////////////////////////////////////////////////////////
// 텍스쳐3

//
D11Texture3::D11Texture3() throw()
{
}

// 
D11Texture3::~D11Texture3() throw()
{
	((D11Rdh*)qgRdh::Instance)->_RoamTexture3Unload(this);
}

//
bool D11Texture3::_Create(D11Rdh* rdh)
{
	//
	D3D11_TEXTURE3D_DESC txd;
	txd.Width = _width;
	txd.Height = _height;
	txd.Depth = _depth;
	txd.MipLevels = _mips;	// 자동밉맵이면 0일 것이다. 아니면 큰일남.
	txd.Format = _dev_fmt;
	if (_insts.dynamic)
	{
		txd.Usage = D3D11_USAGE_DYNAMIC;
		txd.BindFlags = D3D11_BIND_SHADER_RESOURCE;
		txd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
		txd.MiscFlags = 0;
	}
	else
	{
		txd.Usage = D3D11_USAGE_DEFAULT;
		txd.CPUAccessFlags = 0;
		// OFF 없고, AUTOMIP 없음
		txd.BindFlags = D3D11_BIND_SHADER_RESOURCE;
		txd.MiscFlags = 0;
	}

	ID3D11Device* device = rdh->GetDevice();
	if (FAILED(device->CreateTexture3D(&txd, NULL, (ID3D11Texture3D**)&_dev_tex)))
	{
		kobj_tracep(KLV_ERROR, KERR_FAIL_CREATE);
		return false;
	}

	//
	D3D11_SHADER_RESOURCE_VIEW_DESC srvd;
	srvd.Format = _dev_fmt;
	srvd.ViewDimension = D3D_SRV_DIMENSION_TEXTURE3D;
	srvd.Texture3D.MostDetailedMip = 0;
	srvd.Texture3D.MipLevels = _mips;

	ID3D11ShaderResourceView* srv;
	if (FAILED(device->CreateShaderResourceView(_dev_tex, &srvd, &srv)))
	{
		kobj_tracep(KLV_ERROR, KERR_FAIL_CREATE);
		return false;
	}

	_node_desc = K_CAST_PTR_TO_PUINT(srv);

	return true;
}

//
bool D11Texture3::Map(qbTextureMap* map, kint flag, kint mips, kint sliceface)
{
	kobj_tracep(KLV_FATAL, KERR_NOTIMPL);
	return false;
}

//
bool D11Texture3::Unmap()
{
	kobj_tracep(KLV_FATAL, KERR_NOTIMPL);
	return false;
}


//////////////////////////////////////////////////////////////////////////
// 모션 텍스쳐

//
D11AnimTexture::~D11AnimTexture() throw()
{
	// 렌더러 마다 텍스쳐 해제가 다르므로 여기서 함
	if (_mt_preimgs.empty())
		_ClearStreams();
	else
	{
		kForEach(kuintptr n, _mt_preimgs)
		{
			ID3D11ShaderResourceView* srv = (ID3D11ShaderResourceView*)n;
			srv->Release();
		}
	}

	((D11Rdh*)qgRdh::Instance)->_RoamAnimTextureUnload(this);
}

//
bool D11AnimTexture::MotionUpdate(float adv)
{
	kuintptr ptr;
	bool res;

	if (!ManualMotionUpdate(adv, res, ptr))
		return false;

	if (res)
	{
		// 다이나믹이므로 걍 처리
		ID3D11DeviceContext* context = ((D11Rdh*)qgRdh::Instance)->GetContext();
		D3D11_MAPPED_SUBRESOURCE ms;
		if (SUCCEEDED(context->Map(_dev_tex, D3D11CalcSubresource(0, 0, 0), D3D11_MAP_WRITE_DISCARD, 0, &ms)))
		{
			qgImage* img = _mt_ucmimg;
			if ((UINT)img->GetPitch() == ms.RowPitch)
				memcpy(ms.pData, img->GetData(), img->GetSize());
			else
			{
				// 피치는 각 대상의 크기 단위라는 거에 주의
				// 무조건 32비트 RGBA만 가능함
				qgBlitCopy::COPY_32_32(_width, _height, (kint*)img->GetData(), 0, (kint*)ms.pData, ms.RowPitch / 4, false);
			}
			context->Unmap(_dev_tex, D3D11CalcSubresource(0, 0, 0));
		}
	}
	else if (ptr != 0)
	{
		if (ptr != _node_desc)
		{
			ID3D11ShaderResourceView* srv;
			if (_node_desc != 0)
			{
				srv = (ID3D11ShaderResourceView*)_node_desc;
				srv->Release();
			}
			_node_desc = ptr;
			if (ptr)
			{
				srv = (ID3D11ShaderResourceView*)ptr;
				srv->AddRef();
			}
		}
	}

	return true;
}

//
ID3D11ShaderResourceView* D11AnimTexture::_CreateTexture(D11Rdh* rdh, ID3D11Resource** resource)
{
	D3D11_TEXTURE2D_DESC txd;
	txd.Width = _width;
	txd.Height = _height;
	txd.MipLevels = _mips;
	txd.Format = _dev_fmt;
	txd.SampleDesc = _dev_sdc;
	if (_insts.dynamic)
	{
		txd.Usage = D3D11_USAGE_DYNAMIC;
		txd.BindFlags = D3D11_BIND_SHADER_RESOURCE;
		txd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
		txd.MiscFlags = 0;
	}
	else
	{
		txd.Usage = D3D11_USAGE_DEFAULT;
		txd.CPUAccessFlags = 0;
		// 애니메이션은 오프고 오토고 없다
		txd.BindFlags = D3D11_BIND_SHADER_RESOURCE;
		txd.MiscFlags = 0;
	}
	txd.ArraySize = 1;

	ID3D11Device* device = rdh->GetDevice();
	if (FAILED(device->CreateTexture2D(&txd, NULL, (ID3D11Texture2D**)resource)))
	{
		kobj_tracep(KLV_ERROR, KERR_FAIL_CREATE);
		return NULL;
	}

	D3D11_SHADER_RESOURCE_VIEW_DESC srvd;
	srvd.Format = _dev_fmt;
	srvd.Texture2D.MostDetailedMip = 0;
	srvd.Texture2D.MipLevels = _mips;
	srvd.ViewDimension = _dev_sdc.Count > 1 ? D3D_SRV_DIMENSION_TEXTURE2DMS : D3D_SRV_DIMENSION_TEXTURE2D;

	ID3D11ShaderResourceView* srv;
	if (FAILED(device->CreateShaderResourceView(*resource, &srvd, &srv)))
	{
		(*resource)->Release();
		kobj_tracep(KLV_ERROR, KERR_FAIL_CREATE);
		return NULL;
	}

	return srv;
}

//
bool D11AnimTexture::_CreateAnim(const char* filename, bool allocate, D11Rdh* rdh)
{
	// 스트림
	kStream* stm = rdh->GetStub()->FileOpen(filename, true);
	if (stm == NULL)
		return NULL;

	if (!qgFwSupp::CheckHeadAndTail(stm))
	{
		stm->Unload();
		return NULL;
	}

	// 모션
	if (!_ParseMotion(stm, allocate))
		return false;

	// 여기에서 쓴 색깔 포맷은 윈도우8 전용이다!!! 
	// DXGI 1.1 -> 1.0으로 하려면 RGBA를 BGRA로 고쳐야함
	qgImage* img = _mt_ucmimg;

	if (_mt_preimgs.empty())
	{
		// 한장 갖다 잠궈 씀, 영상은 크기 제한함
		if (!_InitDimension(4096, img->GetWidth(), img->GetHeight(), 1, 1, QGCF32_RGBA) ||
			!_InitProperty(rdh, filename, QGTEXTURE_2D, QGTEXF_NOMIP | QGRESF_DYNAMIC))
			return false;

		_node_desc = (kuintptr)_CreateTexture(rdh, &_dev_tex);
		if (_node_desc == 0)
			return false;
	}
	else
	{
		// 모두 만들어 놓기, 마찬가지로 크기 제한
		if (!_InitDimension(4096, img->GetWidth(), img->GetHeight(), 1, 1, QGCF32_RGBA) ||
			!_InitProperty(rdh, filename, QGTEXTURE_2D, QGTEXF_NOMIP))
			return false;

		ID3D11ShaderResourceView* srv;
		ID3D11Resource* res;

		D3D11_SUBRESOURCE_DATA subd;
		subd.pSysMem = img->GetData();
		subd.SysMemPitch = img->GetPitch();
		subd.SysMemSlicePitch = img->GetSize();

		for (kuint i = 0; i < _mt_count; i++)
		{
			if (!_UpdateStreamImage(i))
				srv = NULL;
			else
			{
				srv = _CreateTexture(rdh, &res);
				kunk_release(res);
			}

			_mt_preimgs[i] = (kuintptr)srv;
		}

		// 0번 넣어 둠
		srv = (ID3D11ShaderResourceView*)_mt_preimgs[0];
		kunk_add_ref(srv);

		_node_desc = (kuintptr)srv;

		// 정리
		_ClearStreams();

		_insts.anim_cache = true;
	}

	// 오케
	_insts.beta_anim = true;

	return true;
}


//////////////////////////////////////////////////////////////////////////
// 오프 텍스쳐

//
D11OffTexture::~D11OffTexture() throw()
{
	kunk_release(_rtv);
	kobj_unload(_db);
}

//
bool D11OffTexture::_CreateOff(D11Rdh* rdh)
{
	ID3D11ShaderResourceView* srv = (ID3D11ShaderResourceView*)_node_desc;
	D3D11_SHADER_RESOURCE_VIEW_DESC srvdesc;
	srv->GetDesc(&srvdesc);

	// 렌더 타깃 뷰
	D3D11_RENDER_TARGET_VIEW_DESC rtvdesc;
	memset(&rtvdesc, 0, sizeof(D3D11_RENDER_TARGET_VIEW_DESC));
	rtvdesc.Format = _dev_fmt;

	switch (srvdesc.ViewDimension)
	{
		case D3D_SRV_DIMENSION_BUFFER:
			rtvdesc.ViewDimension = D3D11_RTV_DIMENSION_BUFFER;
			break;

		case D3D_SRV_DIMENSION_TEXTURE1D:
			rtvdesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE1D;
			break;

		case D3D_SRV_DIMENSION_TEXTURE2D:
			rtvdesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
			break;

		case D3D_SRV_DIMENSION_TEXTURE2DMS:
			rtvdesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;
			break;

		case D3D_SRV_DIMENSION_TEXTURE3D:
			rtvdesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D;
			break;

#if 0
		case D3D_SRV_DIMENSION_TEXTURECUBE:
			rtvdesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
			rtvdesc.Texture2DArray.FirstArraySlice = (FACE번호 넣어야함);
			rtvdesc.Texture2DArray.ArraySize = 1;
			rtvdesc.Texture2DArray.MipSlice = 0;
			break;
#endif

		default:
			return false;
	}

	ID3D11RenderTargetView* rtv;
	HRESULT hr = rdh->GetDevice()->CreateRenderTargetView(_dev_tex, &rtvdesc, &rtv);

	if (FAILED(hr))
		return false;

	//
	_rtv = rtv;

	return true;
}

bool D11OffTexture::Enter(kint clearance, const tpl::ColorValue& color, float depth, kuint stencil)
{
	if (_lock_off)
		return false;

	D11Rdh* rdh = (D11Rdh*)qgRdh::Instance;

	//
	rdh->GetOffTexture(_save_cnt, _save_tex);

	//
	qgTexture* rts = this;
	rdh->SetRenderTargets(1, &rts);

	rdh->ClearMRTs(clearance, 1, &color, depth, stencil);

	// 올ㅋ
	_lock_off = true;

	return true;
}

bool D11OffTexture::Leave()
{
	if (!_lock_off)
		return false;

	D11Rdh* rdh = (D11Rdh*)qgRdh::Instance;

	if (_save_cnt == 0)
		rdh->SetRenderTargets(0, NULL);
	else
		rdh->SetRenderTargets(_save_cnt, (qgTexture**)&_save_tex);

	_lock_off = false;

	return true;
}

#endif	// _QG_USE_D11
